POST / 02

数据结构模板

2026-08-27 8 分钟 约 1736 字 #算法竞赛#数据结构

线段树

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int a[N],n,m;
struct Node{
    int l,r,sum,lenth,tag;
}tr[4*N];
void pushup(int x){
    tr[x].sum = tr[tr[x].l].sum + tr[tr[x].r].sum;
    return;
}
void buildtree(int x,int l,int r){
    tr[x] = {l,r,0,r-l+1};
    if(l == r){
        tr[x].sum = a[l];
        return;
    }
    buildtree(x<<1,l,l+r>>1);
    buildtree(x<<1|1,(l+r>>1)+1,r);
    pushup(x);
    return;
}
void pushdown(int x){
    if(!tr[x].tag) return;
    tr[x<<1].tag += tr[x].tag;
    tr[x<<1|1].tag += tr[x].tag;
    tr[x<<1].sum += tr[x].tag * tr[x<<1].lenth;
    tr[x<<1|1].sum += tr[x].tag * tr[x<<1|1].lenth;
    tr[x].tag = 0;
    return;
}
void change(int x,int l,int r,int v){
    int ll = tr[x].l,rr = tr[x].r;
    if(l <= ll && rr <= r){
        tr[x].sum += v*tr[x].lenth;
        tr[x].tag += v;
        return;
    }
    if(rr<l || r < ll) return;
    pushdown(x);
    change(x<<1,l,r,v);
    change(x<<1|1,l,r,v);
    pushup(x);
    return;
}
int query(int x,int l,int r){
    int ll = tr[x].l,rr=tr[x].r;
    if(l <= ll && rr <= r){
        return tr[x].sum;
    }
    if(rr < l || r < ll) return 0;
    pushdown(x);
    return query(x<<1,l,r) + query(x<<1|1,l,r);
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    buildtree(1,1,n);
    while(m--){
        int opt,x,y,k;
        cin>>opt;
        if(opt == 1){
            scanf("%lld%lld%lld",&x,&y,&k);
            change(1,x,y,k);
        }else{
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",query(1,x,y));
        }
    }
    return 0;
}

板子板子板子

可持久化线段树

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int a[N],n,m,tot;
int root[25*N]; //存各版本的root
struct Node{
    int lc,rc,val;  //lc,rc表示左右儿子索引
}tr[25*N];
int build(int l,int r){
    int now = ++tot; //申请一个新编号
    if(l == r){
        tr[now].val = a[l];
        return now;
    }
    int mid = l+r>>1;
    tr[now].lc = build(l,mid);
    tr[now].rc = build(mid+1,r);
    return now; //回传当前节点编号
}
int clone(int old){ // 复制节点
    tr[++tot] = tr[old];
    return tot; // 返回节点编号
}
int update(int old,int l ,int r,int pos,int val){
    int now = clone(old); // 复制节点
    if(l == r){
        tr[now].val = val;
        return now;
    }
    int mid = l+r>>1;
    if(pos <= mid){
        tr[now].lc = update(tr[now].lc,l,mid,pos,val); // 修改左儿子
    }else{
        tr[now].rc = update(tr[now].rc,mid+1,r,pos,val);//同上
    }
    return now; // 返回节点编号
}
int query(int now,int l,int r,int pos){ //Query没什么变化
    if(l == r)
        return tr[now].val;
    int mid = l+r>>1;
    if(pos <= mid){
        return query(tr[now].lc,l,mid,pos);
    }else{
        return query(tr[now].rc,mid+1,r,pos);
    }
}
signed main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    root[0] = build(1,n);
    int cnt = 0;//存root 编号
    while(m--){
        int v,opt;
        scanf("%d%d",&v,&opt);
        if(opt == 1){
            int pos,val;scanf("%d%d",&pos,&val);
            root[++cnt] = update(root[v],1,n,pos,val);
        }else{
            int pos;scanf("%d",&pos);
            printf("%d\n",query(root[v],1,n,pos));
            root[++cnt] = root[v];
        }
    }
    return 0;
}

区别在动态开点,不算太难,空间要开25N,long long看数据范围

FHQ平衡树

核心是split和merge,比起带旋平衡树更好写,常数更优秀,可拓展性强,并且是可持久化平衡树的基础

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5+5;
int n,tot,root;
struct Node{
    int l,r,val,prio;
    int sz;
}tr[N];

int new_node(int val){
    tr[++tot] = {0,0,val,rand(),1};
    return tot;
}
void pushup(int x){
    tr[x].sz = tr[tr[x].l].sz + tr[tr[x].r].sz +1;
}
void split(int now,int val,int &x,int &y){
    if(!now) {x = y = 0; return;}
    if(tr[now].val <= val){
        x = now;
        split(tr[now].r,val,tr[now].r,y);
    }else{
        y = now;
        split(tr[now].l,val,x,tr[now].l);
    }
    pushup(now);
}
int merge(int x,int y){
    if(!x||!y) return x+y;
    if(tr[x].prio > tr[y].prio){
        tr[x].r = merge(tr[x].r,y);
        pushup(x);
        return x;
    }else{
        tr[y].l = merge(x,tr[y].l);
        pushup(y);
        return y;
    }
}
void insert(int v){
    int x,y;
    split(root,v,x,y);
    root = merge(merge(x,new_node(v)),y);
}
void erase(int v){
    int x,y,z;
    split(root,v,x,y);
    split(x,v-1,x,z);
    if(z) z = merge(tr[z].l,tr[z].r);
    root = merge(merge(x,z),y);
}
int get_low(int v){
    int x,y;
    split(root,v-1,x,y);
    int ans = tr[x].sz +1;
    root = merge(x,y);
    return ans;
}
int rank_val(int now,int rk){
    if(tr[tr[now].l].sz >= rk){
        return rank_val(tr[now].l,rk);
    }else if(tr[tr[now].l].sz +1 == rk){
        return tr[now].val;
    }else{
        return rank_val(tr[now].r,rk-tr[tr[now].l].sz -1);
    }
}
int get_pre(int v){
    int x,y;
    split(root,v-1,x,y);
    int ans = rank_val(x,tr[x].sz);
    root = merge(x,y);
    return ans;
}
int get_nxt(int v){
    int x,y;
    split(root,v,x,y);
    int ans = rank_val(y,1);
    root = merge(x,y);
    return ans;
}

int main(){
    scanf("%d",&n);
    while(n--){
        int opt,x;
        scanf("%d%d",&opt,&x);
        opt==1?(insert(x),0):opt==2 ? (erase(x),0) :
        opt==3?(printf("%d\n",get_low(x)),0):
        opt==4 ? (printf("%d\n", rank_val(root,x)),0) :
        opt==5 ? (printf("%d\n", get_pre(x)),0) :
                 (printf("%d\n", get_nxt(x)),0);
    }
    return 0;
}

坑点:

可持久化平衡树

基于FHQ(因为FHQ不修改树的结构)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 4e7+5;
int n,tot;
vector<int>roots;
struct Node{
    int l,r,val,prio;
    int sz;
}tr[N];

int new_node(int val){
    tr[++tot] = {0,0,val,rand(),1};
    return tot;
}
int clone(int now){ //复制节点
    tr[++tot] = tr[now];
    return tot;
}
void pushup(int x){
    tr[x].sz = tr[tr[x].l].sz + tr[tr[x].r].sz +1;
}
void split(int now,int val,int &x,int &y){
    if(!now) {x = y = 0; return;}
    int new_now = clone(now);//复制now节点
    if(tr[now].val <= val){
        x = new_now;
        split(tr[now].r,val,tr[new_now].r,y); //递归处理原节点的儿子
    }else{
        y = new_now;
        split(tr[now].l,val,x,tr[new_now].l);
    }
    pushup(new_now);
}
int merge(int x,int y){
    if(!x||!y) return x+y;
    if(tr[x].prio > tr[y].prio){
        int new_x = clone(x);
        tr[new_x].r = merge(tr[x].r,y);
        pushup(new_x);
        return new_x;
    }else{
        int new_y = clone(y);
        tr[new_y].l = merge(x,tr[y].l);
        pushup(new_y);
        return new_y;
    }
}
int insert(int root,int v){
    int x,y;
    split(root,v,x,y);
    return merge(merge(x,new_node(v)),y);
}
int erase(int root,int v){
    int x,y,z;
    split(root,v,x,y);
    split(x,v-1,x,z);
    if(z) z = merge(tr[z].l,tr[z].r);
    return merge(merge(x,z),y);
}
int get_low(int root,int v){//因为不能直接split&merge,所以改为只读遍历
    int cnt = 0;
    while(root){
        if(tr[root].val < v){
            cnt += tr[tr[root].l].sz +1;
            root = tr[root].r;
        } else{
            root = tr[root].l;
        }
    }
    return cnt+1;
}
int rank_val(int root,int rk){
    while(root){
        if(tr[tr[root].l].sz >= rk){
            root = tr[root].l;
        }else if(tr[tr[root].l].sz == rk-1){
            return tr[root].val;
        }else{
            rk -= tr[tr[root].l].sz +1;
            root = tr[root].r;
        }
    }
    return -1;
}
int get_pre(int root,int v){
    int ans = -1*(int)(1e9)+5;
    while(root){
        if(tr[root].val < v){
            ans = max(ans,tr[root].val);
            root = tr[root].r;
        }else root = tr[root].l;
    }
    return ans;
}
int get_nxt(int root,int v){
    int ans = (int)(1e9)+5;
    while(root){
        if(tr[root].val > v){
            ans = min(ans,tr[root].val);
            root = tr[root].l;
        }else{
            root = tr[root].r;
        }
    }
    return ans;
}

int main(){
    scanf("%d",&n);
    roots.reserve(n+1);
    roots.push_back(0);
    while(n--){
        int v,opt,x;
        scanf("%d%d%d",&v,&opt,&x);
        int cur = roots[v];
        if(opt == 1){
            int new_root = insert(cur,x);
            roots.push_back(new_root);
        }else if(opt == 2){
            int nr = erase(cur,x);
            roots.push_back(nr);
        }else if(opt == 3){
            printf("%d\n",get_low(cur,x));
            roots.push_back(cur);
        }else if(opt == 4){
            printf("%d\n",rank_val(cur,x));
            roots.push_back(cur);
        }else if(opt == 5){
            printf("%d\n",get_pre(cur,x));
            roots.push_back(cur);
        }else{
            printf("%d\n",get_nxt(cur,x));
            roots.push_back(cur);
        }
    }
    return 0;
}

相对比较细小(?)